package com.tiancheng.trade.payment.task.umsdhjt;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.tiancheng.trade.commom.core.enums.PaymentChannelEnum;
import com.tiancheng.trade.commom.core.enums.SettleStatusEnum;
import com.tiancheng.trade.commom.core.exception.Asserts;
import com.tiancheng.trade.commom.core.exception.BusinessException;
import com.tiancheng.trade.commom.core.exception.error.BusinessErrorInfoEnum;
import com.tiancheng.trade.commom.web.feign.MerchantFeignService;
import com.tiancheng.trade.commom.core.utils.DateTimeUtil;
import com.tiancheng.trade.payment.bo.SettleFlowDetailMerchantBO;
import com.tiancheng.trade.payment.channel.umsdhjt.UmsDhjtAllConfig;
import com.tiancheng.trade.payment.channel.umsdhjt.UmsDhjtProperties;
import com.tiancheng.trade.payment.channel.umsdhjt.reponse.UmsDhjtSettleQueryBalancRes;
import com.tiancheng.trade.payment.channel.umsdhjt.reponse.UmsDhjtSettleResponse;
import com.tiancheng.trade.payment.channel.umsdhjt.request.UmsSettleWithdrawalsDetailRequest;
import com.tiancheng.trade.payment.channel.umsdhjt.request.UmsSettleWithdrawalsRequest;
import com.tiancheng.trade.payment.channel.umsdhjt.settle.UmsDhjtSettleClient;
import com.tiancheng.trade.payment.enums.WithdrawalStatusEnum;
import com.tiancheng.trade.payment.model.*;
import com.tiancheng.trade.payment.service.*;
import com.tiancheng.trade.payment.util.helper.PaymentIdHelper;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Component;
import org.springframework.transaction.support.TransactionTemplate;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @Author: kellen
 * @Description: 大华捷通结算任务
 * @Date: create in 2024/12/5 15:11
 */
@Slf4j
@Component
public class UmsDhjtSettleTask {
    /**
     * 单个文件明细数不超过1万条
     */
    private static final int FILE_MAX = 9000;
    private static final String LOST_RULE = "缺少清分规则";
    private static final String LOST_MERCHANT = "缺少商户信息或商户企业用户号";
    private static final String LESS_APPR_AMOUNT = "划付轧差为负";
    private static final String LESS_DIST_AMOUNT = "分账轧差为负";
    private static final String SETTLE_ERROR = "ERROR";
    private static final String SETTLE_ING = "ING";
    private static final String SETTLE_SUCCESS = "SUCCESS";
    // 回盘文件查找判断时间
    public static final String SETTLE_TIME = "05:00:00";
    @Resource
    private TransactionTemplate transactionTemplate;
    @Resource
    private MerchantFeignService merchantFeignService;
    @Resource
    private PaymentFlowDetailService flowDetailService;
    @Resource
    private SettlePlatformService settlePlatformService;
    @Resource
    private RedissonClient redissonClient;
    @Resource
    private PaymentRefundService refundService;
    @Resource
    private UmsDhjtAllConfig umsDhjtAllConfig;
    @Resource
    private UmsDhjtSettleClient umsDhjtSettleClient;
    @Resource
    private PaymentFlowDetailSettleService paymentFlowDetailSettleService;
    @Resource
    private IPaymentFlowDetailSettleDetailService paymentFlowDetailSettleDetailService;
    @Resource
    private PaymentMerchantPlatformService paymentMerchantPlatformService;
    @Resource
    private IRefundSettleDetailService refundSettleDetailService;
    @Resource
    private IRefundSettleService refundSettleService;
    @Resource
    private IWithdrawalService withdrawalService;


    public void doSettle(String param) {
        log.info("银联商务新购物车-> 自动清分任务开始执行：{}", LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
        // 同时刻不允许有多个任务处理相同数据
        RLock lock = redissonClient.getLock("UMS_SHOP_SETTLE_LOCK");
        boolean isLock;
        try {
            isLock = lock.tryLock(30L, TimeUnit.MINUTES);
            if (!isLock) {
                log.error("银联商务新购物车-> 自动清分任务执行结果：锁失败！");
                return;
            }
            // 1、查询等待清分商户信息
            final List<SettleFlowDetailMerchantBO> detailMerchantBOS = paymentFlowDetailSettleService.distinctSettleMerchant(LocalDate.now(), PaymentChannelEnum.UMS_SHOP.getCode());
            if (detailMerchantBOS.isEmpty()) {
                log.info("银联商务新购物车-> 自动清分任务执行结果：无应清分数据！");
                return;
            }

            // 平台收款子商户
            /*final Result<List<MerchantChannelResponseDTO>> orgSubMerchants = merchantFeignService.queryOrgSubMerchant(detailMerchantBOS.stream().map(SettleFlowDetailMerchantBO::getOrgPaymentChannelId).collect(Collectors.toList()));
            if (orgSubMerchants.getData().size() < 1) {
                log.error("银联商务新购物车-> 自动清分任务执行结果：缺少平台子商户！");
                return;
            }
            final Map<Long, MerchantChannelResponseDTO> orgSubMerchantsMap = orgSubMerchants.getData().stream().collect(Collectors.toMap(MerchantChannelResponseDTO::getOrgPaymentChannelId, Function.identity(), (k1, k2) -> k2));*/

            // 2、查询等待提现的明细项
            // 所有-支付结算记录
            List<PaymentFlowDetailSettle> allSettles = new ArrayList<>();
            // 按商户聚合-支付结算明细
            Map<Long, List<PaymentFlowDetailSettleDetail>> paySettlesMap = new HashMap<>();
            queryPayOrderSettleDetails(allSettles, paySettlesMap);
            if (CollectionUtils.isEmpty(allSettles) || MapUtils.isEmpty(paySettlesMap)) {
                log.info("银银联商务新购物车-> 当日无提现订单！");
                return;
            }
            // 3、查询等待轧差的退款结算单
            // 所有-退款结算记录
            List<RefundSettle> allRefundSettles = new ArrayList<>();
            // 按商户聚合-退款结算明细
            Map<Long, List<RefundSettleDetail>> refundSettleMap = new HashMap<>();
            queryRefundOrderSettleDetails(allRefundSettles, refundSettleMap);

            // 4、根据支付需要清分的金额、退款轧差的金额，校验银联侧的金额
            for (Map.Entry<Long, List<PaymentFlowDetailSettleDetail>> payMerchantEntry : paySettlesMap.entrySet()) {
                log.info(">>>>>>>商户清分订单处理开始，商户id：{}，商户名：{} <<<<<<<", payMerchantEntry.getKey(), payMerchantEntry.getValue().get(0).getMerchantName());
                Optional<SettleFlowDetailMerchantBO> first = detailMerchantBOS.stream().filter(m -> m.getMerchantRegId().equals(payMerchantEntry.getKey())).findFirst();
                if (first.isEmpty()) {
                    // 没有查询到对应商户的信息，目前先打印日志吧，后面看要不要设置结算单的失败状态
                    log.info("商户：{} 没有查询到对应的信息，提现流程失败！", payMerchantEntry.getKey());
                    return;
                }
                // 提现单号
                String withdrawalFlowId = PaymentIdHelper.getWithdrawalFlowId(payMerchantEntry.getKey().toString());
                if (CollectionUtils.isEmpty(payMerchantEntry.getValue())) {
                    log.warn("商户无等待清分的支付明细结算单！商户id:{}", payMerchantEntry.getKey());
                    return;
                }
                // 总的要结算的金额
                boolean hashRefundOrder = false;
                int settleSumAmount = payMerchantEntry.getValue().stream().mapToInt(PaymentFlowDetailSettleDetail::getAmount).sum();
                log.info("-------商户当日要应清分支付订单总额：{}", settleSumAmount);
                if (MapUtils.isNotEmpty(refundSettleMap) && refundSettleMap.containsKey(payMerchantEntry.getKey())
                        && CollectionUtils.isNotEmpty(refundSettleMap.get(payMerchantEntry.getKey()))) {
                    // 如果有退款等待轧差的结算单，计算金额
                    int refundSum = refundSettleMap.get(payMerchantEntry.getKey()).stream().mapToInt(RefundSettleDetail::getAmount).sum();
                    log.info("-------商户当日要轧差的退款总额：{}", refundSum);
                    settleSumAmount -= Math.abs(refundSum);
                    hashRefundOrder = true;
                }
                log.info("-------商户当日最终要提现总额：{}", settleSumAmount);
                // 查询银联侧商户余额
                PaymentFlowDetailSettle paymentFlowDetailSettle = allSettles.stream().filter(s -> s.getSettleFlowId().equals(payMerchantEntry.getValue().get(0).getSettleFlowId())).findFirst().get();

                UmsDhjtProperties umsDhjtProperties = umsDhjtAllConfig.getByOrgPayChannelId(paymentFlowDetailSettle.getOrgPaymentChannelId());

                UmsDhjtSettleQueryBalancRes queryBalanceRes = umsDhjtSettleClient.queryBalance(umsDhjtProperties, paymentFlowDetailSettle.getSubMerchantId());
                if (null == queryBalanceRes || Objects.isNull(queryBalanceRes.getData())) {
                    log.warn("商户可提现余额查询失败！商户id:{}", payMerchantEntry.getKey());
                    return;
                }

                Asserts.isTrue(Objects.nonNull(queryBalanceRes.getData()), "商户余额查询失败！");
                // 待提现金额
                Integer moneyPay = queryBalanceRes.getData().getMoneyPay();
                if (settleSumAmount > moneyPay) {
                    // 如果余额不够，不进行提现
                    log.info("商户提现余额不足，当日不处理！计划提现金额：{}，账户余额：{}", settleSumAmount, moneyPay);
                    continue;
                }


                // 结算批次号
                final String settleBatchNo = PaymentIdHelper.getSettleBatchNo(payMerchantEntry.getKey());

                final UmsSettleWithdrawalsRequest withdrawalsRequest = new UmsSettleWithdrawalsRequest();
                withdrawalsRequest.setTxnDate(DateTimeUtil.getCurrentDay());
                withdrawalsRequest.setTransMid(umsDhjtProperties.getTransMid());//交易商户号
                withdrawalsRequest.setBatchNo(settleBatchNo);//提现批次号
                withdrawalsRequest.setTransType("withdrawals");

                ArrayList<UmsSettleWithdrawalsDetailRequest> details = new ArrayList<>();
                payMerchantEntry.getValue().stream().collect(Collectors.groupingBy(PaymentFlowDetailSettleDetail::getSettleFlowId)).forEach((settleFlowId, settleDetails) -> {
                    for (PaymentFlowDetailSettleDetail settleDetail : settleDetails) {
                        final UmsSettleWithdrawalsDetailRequest wdr = new UmsSettleWithdrawalsDetailRequest();
                        wdr.setTxnNo(settleDetail.getSubSettleFlowId());
                        wdr.setMerOrderId(settleDetail.getSubFlowId());
                        // 结算日期明天
                        wdr.setClearDate(DateTimeUtil.getCurrentDay());
                        wdr.setMerNo(settleDetail.getSubMerchantId());
                        wdr.setMerName(settleDetail.getMerchantName());
                        // 使用收款账户信息的类型（00：使用上送的不校验 BMS；01：使用上送的校验 VMS；02：使用 BMS 的）
                        wdr.setUseAcctType("02");
                        wdr.setTxnAmt(settleDetail.getAmount());
                        wdr.setTxnType("00");
                        wdr.setRemark(String.format("商户：%s-%s日清算所得：%d", wdr.getMerName(), wdr.getClearDate(), wdr.getTxnAmt()));
                        // 提现手续费收取类型，0-外扣（打给商户钱是接口上送的提现金额字段值）、1-内扣（打给商户钱是接口上送的提现金额字段值减去提现手续 费）
                        wdr.setFeeTxCalcType(1);
                        wdr.setFeeTx(0);
                        details.add(wdr);
                    }
                });

                withdrawalsRequest.setSndTime(DateTimeUtil.getCurrentTime());
                withdrawalsRequest.setTotalQty(details.size());//总笔数
                int totalAmount = 0;
                for (UmsSettleWithdrawalsDetailRequest detail : details) {
                    totalAmount += detail.getFeeTx();
                }
                withdrawalsRequest.setTotalAmt(totalAmount);//总金额
                //9000：处理中(表示受理成功) 其余编码视为失败
                boolean finalHashRefundOrder = hashRefundOrder;
                int finalSettleSumAmount = settleSumAmount;
                transactionTemplate.execute(transactionStatus -> {
                    try {
                        // 更新退款流水状态
                        final UmsDhjtSettleResponse upload = umsDhjtSettleClient.upload(umsDhjtProperties, withdrawalsRequest);
                        log.error("大华捷通渠道，上传银商分钟信息返回失败：{}", upload);
                        BusinessErrorInfoEnum.SYSTEM_ERROR.assertIsTrue(upload.getTxnRespCode().equals("9000"));

                        // 提现成功
                        PaymentFlowDetailSettleDetail pfdsd = new PaymentFlowDetailSettleDetail();
                        pfdsd.setSettleStatus(SettleStatusEnum.ING.getCode());
                        pfdsd.setWithdrawalId(withdrawalFlowId);
                        paymentFlowDetailSettleDetailService.update(pfdsd, new LambdaQueryWrapper<PaymentFlowDetailSettleDetail>()
                                .in(PaymentFlowDetailSettleDetail::getId, payMerchantEntry.getValue().stream().map(PaymentFlowDetailSettleDetail::getId).collect(Collectors.toSet())));

                        PaymentFlowDetailSettle updatePaySettle = new PaymentFlowDetailSettle();
                        updatePaySettle.setSettleStatus(SettleStatusEnum.ING.getCode() );
                        updatePaySettle.setBatchNo(settleBatchNo);
                        paymentFlowDetailSettleService.update(updatePaySettle, new LambdaQueryWrapper<PaymentFlowDetailSettle>().in(PaymentFlowDetailSettle::getSettleFlowId, payMerchantEntry.getValue().stream().map(PaymentFlowDetailSettleDetail::getSubFlowId).collect(Collectors.toSet())));
                        if (finalHashRefundOrder) {
                            RefundSettleDetail rsd = new RefundSettleDetail();
                            rsd.setSettleStatus(SettleStatusEnum.ING.getCode());
                            rsd.setWithdrawalId(withdrawalFlowId);
                            refundSettleDetailService.update(rsd, new LambdaQueryWrapper<RefundSettleDetail>()
                                    .in(RefundSettleDetail::getId, refundSettleMap.get(payMerchantEntry.getKey()).stream().map(RefundSettleDetail::getId).collect(Collectors.toSet())));
                        }
                        // 保存提现记录
                        Withdrawal withdrawal = new Withdrawal();
                        withdrawal.setWithdrawalId(withdrawalFlowId);
                        withdrawal.setSsn(StringUtils.isEmpty(upload.getBatchNo()) ? "" : upload.getBatchNo());
                        withdrawal.setMerchantRegId(payMerchantEntry.getKey());
                        withdrawal.setMerchantCode(payMerchantEntry.getValue().get(0).getMerchantCode());
                        withdrawal.setMerchantName(payMerchantEntry.getValue().get(0).getMerchantName());
                        withdrawal.setSubMerchantId(payMerchantEntry.getValue().get(0).getSubMerchantId());
                        withdrawal.setEnterpriseCode(payMerchantEntry.getValue().get(0).getSettleMerchantId());
                        withdrawal.setAmount(finalSettleSumAmount);
                        withdrawal.setFee(0);
                        withdrawal.setWithdrawalStatus(WithdrawalStatusEnum.ING.getCode());
                        withdrawal.setOrgPaymentChannelId(first.get().getOrgPaymentChannelId());
                        withdrawal.setPaymentChannel(first.get().getPaymentChannel());
                        withdrawal.setCompleteTime(LocalDateTime.now());
                        withdrawal.setUpdateTime(LocalDateTime.now());
                        withdrawal.setCreateTime(LocalDateTime.now());
                        withdrawalService.save(withdrawal);
                        return upload.getTxnRespCode().equals("9000");
                    } catch (Exception e) {
                        transactionStatus.setRollbackOnly();
                        throw e;
                    }
                });
            }
        } catch (Exception e) {
            log.error("银联商务新购物车-> 提现自动任务执行失败：", e);
            throw new BusinessException(BusinessErrorInfoEnum.UMS_SETTLE_ERROR, null, "银联商务新购物车-> 提现自动任务执行失败", e);
        } finally {
            if (lock.isLocked()) {
                lock.unlock();
            }
        }
    }


    /**
     * 汇总当前等待轧差的退款结算单
     *
     * @param allRefundSettles               所有退款结算单
     * @param merchantRefundSettleDetailsMap 按商户汇总的退款结算明细
     */
    private void queryRefundOrderSettleDetails(List<RefundSettle> allRefundSettles, Map<Long, List<RefundSettleDetail>> merchantRefundSettleDetailsMap) {
        long size = 1000L; // 每批次查询起始id
        long current = 1;// 当前页
        List<RefundSettle> list;
        do {
            // 3.1、抓取当日应清分子订单和滞留子订单，每次抓取1000条
            IPage<RefundSettle> refundSettleIPage = refundSettleService.listWaitSettles(LocalDate.now(), PaymentChannelEnum.UMS_SHOP.getCode(), current, size);
            list = refundSettleIPage.getRecords();
            if (CollectionUtils.isEmpty(list)) {
                log.info("未查询到待结算订单或待结算订单处理完毕！");
                break;
            }
            // 支付流水清分明细项
            List<RefundSettleDetail> refundSettleDetails = refundSettleDetailService.queryBySettleIds(list.stream().map(RefundSettle::getId).collect(Collectors.toSet()));
            allRefundSettles.addAll(list);
            current++;
            // 按商户分组所有的结算明细项
            if (CollectionUtils.isEmpty(refundSettleDetails)) {
                continue;
            }
            // 按商户分组所有的结算明细项
            Map<Long, List<RefundSettleDetail>> merchantSettles = refundSettleDetails.stream().collect(Collectors.groupingBy(RefundSettleDetail::getMerchantRegId));
            if (!merchantSettles.isEmpty()) {
                merchantSettles.forEach((merchantId, refundFlowDetailSettleDetails) -> {
                    if (merchantRefundSettleDetailsMap.containsKey(merchantId) && CollectionUtils.isNotEmpty(merchantRefundSettleDetailsMap.get(merchantId))) {
                        merchantRefundSettleDetailsMap.get(merchantId).addAll(refundFlowDetailSettleDetails);
                    } else {
                        merchantRefundSettleDetailsMap.put(merchantId, refundFlowDetailSettleDetails);
                    }
                });
            }
        } while (list.size() > 999);
    }

    /**
     * 汇总当前等待提现的订单明细
     *
     * @param allSettles         返回的所有等待提现的订单
     * @param merchantSettlesMap 按商户维度汇总的所有等待提现的商户明细
     */
    private void queryPayOrderSettleDetails(List<PaymentFlowDetailSettle> allSettles, Map<Long, List<PaymentFlowDetailSettleDetail>> merchantSettlesMap) {
        long size = 1000L; // 每批次查询起始id
        long current = 1;// 当前页
        List<PaymentFlowDetailSettle> list;
        do {
            // 3.1、抓取当日应清分子订单和滞留子订单，每次抓取1000条
            IPage<PaymentFlowDetailSettle> flowDetailSettleIPage = paymentFlowDetailSettleService.listWaitSettles(LocalDate.now(), PaymentChannelEnum.UMS_SHOP.getCode(), current, size);
            list = flowDetailSettleIPage.getRecords();
            if (CollectionUtils.isEmpty(list)) {
                log.info("未查询到待结算订单或待结算订单处理完毕！");
                break;
            }
            // 支付流水清分明细项
            List<PaymentFlowDetailSettleDetail> flowDetailSettleDetails = paymentFlowDetailSettleDetailService.queryWaitSettleDetailsBySettleIds(list.stream().map(PaymentFlowDetailSettle::getId).collect(Collectors.toSet()));
            allSettles.addAll(list);
            current++;
            // 按商户分组所有的结算明细项
            if (CollectionUtils.isEmpty(flowDetailSettleDetails)) {
                continue;
            }
            Map<Long, List<PaymentFlowDetailSettleDetail>> merchantSettles = flowDetailSettleDetails.stream().collect(Collectors.groupingBy(PaymentFlowDetailSettleDetail::getMerchantRegId));
            if (!merchantSettles.isEmpty()) {
                merchantSettles.forEach((merchantId, paymentFlowDetailSettleDetails) -> {
                    if (merchantSettlesMap.containsKey(merchantId) && CollectionUtils.isNotEmpty(merchantSettlesMap.get(merchantId))) {
                        merchantSettlesMap.get(merchantId).addAll(paymentFlowDetailSettleDetails);
                    } else {
                        merchantSettlesMap.put(merchantId, paymentFlowDetailSettleDetails);
                    }
                });
            }
        } while (list.size() > 999);
    }

    private String getRemark(String merchantName) {
        String str = StringUtils.isNotEmpty(merchantName) ? merchantName + "分账所得" : "商户分账所得";
        return str.length() >= 20 ? str.substring(0, 19) : str;
    }

    /*
    public void doSettle(String param) {
        log.info("自动清分任务开始执行：{}", LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
        // 同时刻不允许有多个任务处理相同数据
        RLock lock = redissonClient.getLock("UMS_DHJT_UPLOAD_SETTLE");
        boolean isLock;
        try {
            isLock = lock.tryLock(30L, TimeUnit.MINUTES);
            if (!isLock) {
                log.error("大华捷通渠道-> 自动清分任务执行结果：锁失败！");
                return;
            }
            // 1、查询等待清分商户信息
            final List<SettleFlowDetailMerchantBO> detailMerchantBOS = paymentFlowDetailSettleService.distinctSettleMerchant(LocalDate.now(), PaymentChannelEnum.UMS_SHOP.getCode());
            if (detailMerchantBOS.isEmpty()) {
                log.info("银联商务新购物车-> 自动清分任务执行结果：无应清分数据！");
                return;
            }
            // 2、查询商户企业用户号
            List<MerchantChannelResponseDTO> merRepResult = paymentMerchantPlatformService.queryByMerchantCodeAndChannel(detailMerchantBOS.stream().map(d -> {
                final MerchantChannelRequestDTO merchantChannelRequestDTO = new MerchantChannelRequestDTO();
                merchantChannelRequestDTO.setBusinessCode(d.getBusinessCode());
                merchantChannelRequestDTO.setOrgPaymentChannelId(d.getOrgPaymentChannelId());
                merchantChannelRequestDTO.setPaymentChannelCode(d.getPaymentChannel());
                merchantChannelRequestDTO.setMerchantCode(d.getMerchantCode());
                return merchantChannelRequestDTO;
            }).collect(Collectors.toList()));

            if (CollectionUtils.isEmpty(merRepResult)) {
                log.error("大华捷通渠道-> 自动清分任务执行结果：缺少渠道商户信息！");
                return;
            }

            Map<String, MerchantChannelResponseDTO> merchantMap = new HashMap<>();
            for (MerchantChannelResponseDTO datum : merRepResult) {
                merchantMap.put(datum.getMerchantCode(), datum);
            }
            final Result<List<MerchantChannelResponseDTO>> orgSubMerchants = merchantFeignService.queryOrgSubMerchant(detailMerchantBOS.stream().map(SettleFlowDetailMerchantBO::getOrgPaymentChannelId).collect(Collectors.toList()));
            if (orgSubMerchants.getData().size() < 1) {
                log.error("大华捷通渠道-> 自动清分任务执行结果：缺少平台子商户！");
                return;
            }
            final Map<Long, MerchantChannelResponseDTO> orgSubMerchantsMap = orgSubMerchants.getData().stream().collect(Collectors.toMap(MerchantChannelResponseDTO::getOrgPaymentChannelId, Function.identity(), (k1, k2) -> k2));
            // 3、查询等待清分的流水信息
            List<PaymentFlowDetailBO> list;
            final List<PaymentFlowDetailSettle> detailSettles = new ArrayList<>();
            final List<PaymentFlowDetailSettleDetail> detailSettlesDetails = new ArrayList<>();
            List<Long> lrList = new ArrayList<>(); // 不可正常清分指令明细记录：缺少清分规则
            List<Long> lmList = new ArrayList<>(); // 不可正常清分指令明细记录：缺少商户信息
            long startId = 0L; // 每批次查询起始id
            do {
                // 3.1、抓取当日应清分子订单和滞留子订单，每次抓取1000条
                list = flowDetailService.listSettle(LocalDate.now(), PaymentChannelEnum.UMS_DHJT.getCode(), startId);
                if (CollectionUtils.isEmpty(list)) {
                    break;
                }
                final Map<String, SettlementRuleResVO> rules = settlePlatformService.querySettleRules(list.stream().map(PaymentFlowDetailBO::getSettleRuleCode).collect(Collectors.toSet()));
                for (PaymentFlowDetailBO flowDetail : list) {
                    final PaymentFlowDetailSettle flowDetailSettle = new PaymentFlowDetailSettle();
                    flowDetailSettle.setOrgPaymentChannelId(flowDetail.getOrgPaymentChannelId());
                    flowDetailSettle.setFlowId(flowDetail.getFlowId());
                    flowDetailSettle.setSubFlowId(flowDetail.getSubFlowId());
                    flowDetailSettle.setMerchantCode(flowDetail.getMerchantCode());
                    flowDetailSettle.setSubMerchantId(flowDetail.getEnterpriseCode());
                    flowDetailSettle.setSettleRuleType(flowDetail.settleRuleTypeEnum().getCode());
                    flowDetailSettle.setSettleRuleCode(flowDetail.getSettleRuleCode());
                    flowDetailSettle.setSubOrderAmount(flowDetail.getOrderAmount());
                    flowDetailSettle.setPlatformAmount(0);
                    flowDetailSettle.setSettleStatus(SettleStatusEnum.ING.getCode());
                    flowDetailSettle.setCreateTime(LocalDateTime.now());
                    flowDetailSettle.setSettleDate(LocalDate.now());
                    flowDetailSettle.setSettleFlowId(PaymentIdHelper.getSettleFlowId(flowDetail.getSubFlowId(),""));
                    final MerchantChannelResponseDTO merchantInfo = merchantMap.get(flowDetail.getMerchantCode());
                    if (Objects.isNull(merchantInfo) || Objects.isNull(merchantInfo.getSubMchId())) {
                        // 缺少清分商户配置
                        lmList.add(flowDetail.getId());
                        flowDetailSettle.setSettleStatus(SettleStatusEnum.FAIL.getCode());
                        flowDetailSettle.setSettleStatusDescribe("缺少清分商户配置");
                        continue;
                    }
                    final SettlementRuleResVO rule = rules.get(flowDetail.getSettleRuleCode());
                    if (Objects.isNull(rule) || Objects.isNull(rule.getSettleRuleCode())) {
                        // 缺少清分规则
                        lrList.add(flowDetail.getId());
                        flowDetailSettle.setSettleStatus(SettleStatusEnum.FAIL.getCode());
                        flowDetailSettle.setSettleStatusDescribe("缺少清分规则配置");
                        continue;
                    }
                    // 平台佣金计算
                    final int platformAmount = settlePlatformService.platformAmountCommissionMerchantSettleRule(flowDetail.getOrderAmount(), rule);
                    final int merchantAmount = flowDetail.getOrderAmount() - platformAmount;
                    // 明细项
                    flowDetailSettle.setPlatformAmount(0);
                    final MerchantChannelResponseDTO subMerchant = orgSubMerchantsMap.get(flowDetail.getOrgPaymentChannelId());
                    if (subMerchant == null) {
                        // 缺少平台清分商户配置
                        lmList.add(flowDetail.getId());
                        flowDetailSettle.setSettleStatus(SettleStatusEnum.FAIL.getCode());
                        flowDetailSettle.setSettleStatusDescribe("缺少平台清分商户（平台抽佣商户）配置");
                        continue;
                    }
                    final PaymentFlowDetailSettleDetail merchantDetail = new PaymentFlowDetailSettleDetail();
                    merchantDetail.setSubMerchantId(flowDetailSettle.getSubFlowId());
                    merchantDetail.setSubMerchantType(1);
                    merchantDetail.setMerchantCode(flowDetailSettle.getMerchantCode());
                    merchantDetail.setMerchantName(subMerchant.getMerchantName());
                    merchantDetail.setSubMerchantId(subMerchant.getSubMchId());
                    merchantDetail.setAmount(merchantAmount);
                    merchantDetail.setSettleFlowId(flowDetailSettle.getSettleFlowId());
                    merchantDetail.setSubSettleFlowId(flowDetailSettle.getSettleFlowId() + "01");
                    detailSettlesDetails.add(merchantDetail);
                    final PaymentFlowDetailSettleDetail platformDetail = new PaymentFlowDetailSettleDetail();
                    platformDetail.setSubMerchantId(flowDetailSettle.getSubFlowId());
                    platformDetail.setSubMerchantType(0);
                    platformDetail.setMerchantCode(flowDetailSettle.getMerchantCode());
                    platformDetail.setSubMerchantId(subMerchant.getSubMchId());
                    platformDetail.setAmount(merchantAmount);
                    platformDetail.setSettleFlowId(flowDetailSettle.getSettleFlowId());
                    platformDetail.setSubSettleFlowId(flowDetailSettle.getSettleFlowId() + "02");
                    detailSettlesDetails.add(platformDetail);

                    detailSettles.add(flowDetailSettle);
                }
            } while (list.size() > 999);
            // 4、清分失败的、成功的入库处理
            // 4.1、更新“缺少清分规则”
            if (!lrList.isEmpty()) {
                flowDetailService.updateSettleBatch(lrList, new HashMap<String, Object>() {{
                    put("settle_status", SETTLE_ERROR);
                    put("settle_error_desc", LOST_RULE);
                }});
            }
            // 4.9、更新“缺少商户信息或商户企业用户号”
            if (!lmList.isEmpty()) {
                flowDetailService.updateSettleBatch(lmList, new HashMap<String, Object>() {{
                    put("settle_status", SETTLE_ERROR);
                    put("settle_error_desc", LOST_MERCHANT);
                }});
            }
            if (CollectionUtils.isNotEmpty(detailSettles)) {
                // 生成批次号
                detailSettles.stream().collect(Collectors.groupingBy(PaymentFlowDetailSettle::getOrgPaymentChannelId)).forEach((orgPaymentChannelId, fds) -> {
                    // 结算批次号
                    final String settleBatchNo = PaymentIdHelper.getSettleBatchNo(orgPaymentChannelId);
                    // 根据配置查询
                    final UmsDhjtProperties umsDhjtProperties = umsDhjtAllConfig.getByOrgPayChannelId(orgPaymentChannelId);

                    final UmsSettleWithdrawalsRequest withdrawalsRequest = new UmsSettleWithdrawalsRequest();
                    withdrawalsRequest.setTxnDate(DateTimeUtil.getCurrentDay());
                    withdrawalsRequest.setTransMid(umsDhjtProperties.getTransMid());//交易商户号
                    withdrawalsRequest.setBatchNo(settleBatchNo);//提现批次号
                    withdrawalsRequest.setTransType("withdrawals");

                    final List<UmsSettleWithdrawalsDetailRequest> details = new ArrayList<>(fds.size());
                    final List<PaymentFlowDetailSettleDetail> insertSettleDetails = detailSettlesDetails.stream().filter(d -> fds.stream().map(PaymentFlowDetailSettle::getSettleFlowId).collect(Collectors.toSet()).contains(d.getSettleFlowId())).toList();
                    for (PaymentFlowDetailSettle fd : fds) {
                        insertSettleDetails.stream().collect(Collectors.groupingBy(PaymentFlowDetailSettleDetail::getSettleFlowId)).forEach((settleFlowId, settleDetails) -> {
                            for (PaymentFlowDetailSettleDetail settleDetail : settleDetails) {
                                final UmsSettleWithdrawalsDetailRequest wdr = new UmsSettleWithdrawalsDetailRequest();
                                wdr.setTxnNo(settleDetail.getSubSettleFlowId());
                                wdr.setMerOrderId(fd.getSubFlowId());
                                // 结算日期明天
                                wdr.setClearDate(DateTimeUtil.getCurrentDay());
                                wdr.setMerNo(fd.getSubMerchantId());
                                wdr.setMerName(settleDetail.getMerchantName());
                                // 使用收款账户信息的类型（00：使用上送的不校验 BMS；01：使用上送的校验 VMS；02：使用 BMS 的）
                                wdr.setUseAcctType("02");
                                wdr.setTxnAmt(settleDetail.getAmount());
                                wdr.setTxnType("00");
                                wdr.setRemark(String.format("商户：%s-%s日清算所得：%d", wdr.getMerName(), wdr.getClearDate(), wdr.getTxnAmt()));
                                // 提现手续费收取类型，0-外扣（打给商户钱是接口上送的提现金额字段值）、1-内扣（打给商户钱是接口上送的提现金额字段值减去提现手续 费）
                                wdr.setFeeTxCalcType(1);
                                wdr.setFeeTx(0);
                                details.add(wdr);
                            }
                        });
                    }
                    withdrawalsRequest.setSndTime(DateTimeUtil.getCurrentTime());
                    withdrawalsRequest.setTotalQty(details.size());//总笔数
                    int totalAmount = 0;
                    for (UmsSettleWithdrawalsDetailRequest detail : details) {
                        totalAmount += detail.getFeeTx();
                    }
                    withdrawalsRequest.setTotalAmt(totalAmount);//总金额
                    //9000：处理中(表示受理成功) 其余编码视为失败
                    transactionTemplate.execute(transactionStatus -> {
                        try {
                            paymentFlowDetailSettleService.saveBatch(fds);
                            paymentFlowDetailSettleDetailService.saveBatch(insertSettleDetails);
                            // 更新支付流水、退款流水状态
                            flowDetailService.updateSettleBatchBySubFlowId(fds.stream().map(PaymentFlowDetailSettle::getSubFlowId).collect(Collectors.toList()), new HashMap<String, Object>() {{
                                put("settle_status", SETTLE_ING);
                                put("settle_error_desc", null);
                            }});
                            // 更新退款流水状态
                            final UmsDhjtSettleResponse upload = umsDhjtSettleClient.upload(umsDhjtProperties, withdrawalsRequest);
                            log.error("大华捷通渠道，上传银商分钟信息返回失败：{}", upload);
                            BusinessErrorInfoEnum.SYSTEM_ERROR.assertIsTrue(upload.getTxnRespCode().equals("9000"));
                            return upload.getTxnRespCode().equals("9000");
                        } catch (Exception e) {
                            transactionStatus.setRollbackOnly();
                            throw e;
                        }
                    });
                });
            }
            // 5、数据上报银商
        } catch (Exception e) {
            log.error("银联资管清分自动任务执行失败：", e);
            throw new BusinessException(BusinessErrorInfoEnum.UMS_SETTLE_ERROR, null, "大华捷通渠道-> 银联资管清分自动任务执行失败", e);
        } finally {
            if (lock.isLocked()) {
                lock.unlock();
            }
        }
        return;
    }*/

}
